# -*- coding: utf-8 -*-
"""
Created on Wed Nov 12 16:22:36 2025

@author: ymh
"""

import pandas as pd
import networkx as nx
import matplotlib.pyplot as plt
import re
import jieba
import os
from tqdm import tqdm

# --------------------------
# 1. 数据加载与预处理
# --------------------------
try:
    df = pd.read_csv('douban_movie_comments.csv', encoding='utf-8-sig')
    print(f"成功加载原始评论数据，共 {len(df)} 条记录")
except FileNotFoundError:
    print("错误：未找到 douban_movie_comments.csv，请确认文件路径正确")
    exit()

# 情感映射
df['评分'] = pd.to_numeric(df['评分'], errors='coerce')
def map_sentiment(score):
    if pd.isna(score): 
        return 'neutral'
    elif score <= 2:
        return 'negative'
    elif score == 3:
        return 'neutral'
    else: 
        return 'positive'
df['sentiment'] = df['评分'].apply(map_sentiment)

# 文本清洗与分词
stopwords = set([
    '的', '了', '是', '在', '这', '都', '让', '我们', '一种', '不是', '没有', '更',
    '但', '与', '中', '为', '用', '当', '于', '以', '将', '成', '对', '自己','很多',
    '就是','什么','最后','《', '》', '：', '——', '、', ',', '.', '！', '？', '看',
    '非常', '很', '也', '还', '就', '有', '把', '给', '说', '你', '我', '她', '他',
    '它', '一个', '一些', '一点','因为','有点','他们',
    '电影', '影片', '觉得', '剧情', '故事', '画面', '演员', '演技', '镜头', '配乐',
    '特效', '导演', '编剧', '类型', '题材', '票房', '上映',
    '观众', '影院', '系列', '续集', '前传', '改编', '原著', '小说', '漫画', '游戏',
    'IP', '角色', '人物', '主角', '配角', '反派','作品','一部',
    '知道', '了解', '认识', '喜欢', '讨厌', '想要', '需要', '应该', '可以', '能够',
    '会', '要', '没', '不', '别', '太', '挺', '比较', '稍微','怎么','还是','还有',
    '那么','这种','这个','这样','但是','这么','感觉','为了','现在','不停','可能'
])

def preprocess_text(text):
    if pd.isna(text) or not isinstance(text, str):
        return []
    text = re.sub(r'[^\u4e00-\u9fa5a-zA-Z0-9\s]', '', str(text).strip())
    text = re.sub(r'\s+', ' ', text)
    words = jieba.lcut(text)
    return [word for word in words if word not in stopwords and len(word) > 1 and not word.isdigit()]

# 按情感分类文本
positive_docs = df[df['sentiment'] == 'positive']['评论内容'].apply(preprocess_text).tolist()
negative_docs = df[df['sentiment'] == 'negative']['评论内容'].apply(preprocess_text).tolist()

# --------------------------
# 2. 构建共现网络
# --------------------------
def build_cooccurrence_network(docs, window_size=3, min_count=5):
    G = nx.Graph()
    word_counts = {}
    cooccurrence = {}

    # 统计词频和共现
    for doc in tqdm(docs, desc="构建共现网络"):
        for i, word in enumerate(doc):
            word_counts[word] = word_counts.get(word, 0) + 1
            for j in range(i + 1, min(i + window_size, len(doc))):
                neighbor = doc[j]
                pair = tuple(sorted([word, neighbor]))
                cooccurrence[pair] = cooccurrence.get(pair, 0) + 1

    # 过滤低频词和低频共现
    for pair, count in cooccurrence.items():
        w1, w2 = pair
        if word_counts[w1] >= min_count and word_counts[w2] >= min_count and count >= min_count:
            G.add_edge(w1, w2, weight=count)

    return G

# 构建正面评论语义网络
pos_network = build_cooccurrence_network(positive_docs, window_size=4, min_count=3)
# 构建负面评论语义网络
neg_network = build_cooccurrence_network(negative_docs, window_size=4, min_count=3)

# --------------------------
# 3. 可视化语义网络
# --------------------------
def plot_semantic_network(G, title, filename):
    if G.number_of_nodes() == 0:
        print(f"无{title}语义网络数据，跳过可视化")
        return
    plt.figure(figsize=(12, 10))
    pos = nx.spring_layout(G, k=0.3)
    edges = G.edges(data=True)
    weights = [edata['weight'] for _, _, edata in edges]
    nx.draw_networkx_nodes(G, pos, node_size=500, node_color='skyblue')
    nx.draw_networkx_edges(G, pos, edgelist=edges, width=[w/5 for w in weights], edge_color='gray')
    nx.draw_networkx_labels(G, pos, font_size=10, font_family='SimHei')
    plt.title(title, fontsize=16, fontfamily='SimHei')
    plt.axis('off')
    plt.savefig(filename, dpi=300, bbox_inches='tight')
    plt.show()

# 可视化正面评论语义网络
if pos_network.number_of_nodes() > 0:
    plot_semantic_network(pos_network, "正面评论语义网络", "positive_semantic_network.png")
else:
    print("正面评论无有效语义网络数据")

# 可视化负面评论语义网络
if neg_network.number_of_nodes() > 0:
    plot_semantic_network(neg_network, "负面评论语义网络", "negative_semantic_network.png")
else:
    print("负面评论无有效语义网络数据")

# --------------------------
# 4. 网络统计指标
# --------------------------
def print_network_stats(G, title):
    if G.number_of_nodes() == 0:
        return
    print(f"\n{title}语义网络统计：")
    print(f"节点数：{G.number_of_nodes()}")
    print(f"边数：{G.number_of_edges()}")
    print(f"平均度：{sum(dict(G.degree()).values()) / G.number_of_nodes():.2f}")
    print(f"网络密度：{nx.density(G):.4f}")

print_network_stats(pos_network, "正面评论")
print_network_stats(neg_network, "负面评论")

# --------------------------
# 5. 关键节点分析（度数中心性）
# --------------------------
def get_top_nodes(G, top_n=5, title=""):
    if G.number_of_nodes() == 0:
        return
    degree_centrality = nx.degree_centrality(G)
    top_nodes = sorted(degree_centrality.items(), key=lambda x: x[1], reverse=True)[:top_n]
    print(f"\n{title}语义网络Top{top_n}关键节点：")
    for node, centrality in top_nodes:
        print(f"{node}: 中心性 {centrality:.4f}")

get_top_nodes(pos_network, top_n=5, title="正面评论")
get_top_nodes(neg_network, top_n=5, title="负面评论")